﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;

namespace uam_fps_game.Gfx
{
    /// <summary>
    /// Class responsible for handling and optimizing shader parameter passing between the application and the graphics card.
    /// </summary>
    public class SimpleEffectManager
    {
        /// <summary>
        /// An instance of SimpleEffect.fx which an instance of this class will be resposible for.
        /// </summary>
        Effect mSimpleEffect;

        // Below are the EffectParameter objects which represent references to a given SimpleEffect parameter.

        // Matrices
        EffectParameter mWorldMatrix;
        EffectParameter mViewMatrix;
        EffectParameter mProjectionMatrix;
        EffectParameter mWorldMatrixInvTrans;

        // Camera
        EffectParameter mCameraPosition;

        // Flags
        EffectParameter mTextureFlags;

        // Colors
        EffectParameter mDiffuseColor;
        EffectParameter mSpecularColorPower;
        EffectParameter mEmissiveColor;

        // Light Data
        const int mMaxLightNum = 16;

        EffectParameter mLightNum;

        /// <summary>
        /// The LightData structure which is identical to the one located in the shader.
        /// </summary>
        struct LightData
        {
            public EffectParameter mLightData_Type;
            public EffectParameter mLightData_Position;
            public EffectParameter mLightData_Direction;
            public EffectParameter mLightData_DiffuseColor;
            public EffectParameter mLightData_LightParams;
            public EffectParameter mLightData_IsGeneratingShadows;
        }

        LightData[] lightData = new LightData[mMaxLightNum];

        EffectParameter mAmbientColorPower;

        // Textures
        EffectParameter mDiffuseTexture;
        EffectParameter mNormalTexture;
        EffectParameter mSpecularTexture;
        EffectParameter mParallaxTexture;
        EffectParameter mShadowMapTexture;

        EffectParameter mParallaxScaleBias;

        public SimpleEffectManager(Effect simpleEffect)
        {

            mSimpleEffect = simpleEffect;

            // Matrices
            mWorldMatrix = mSimpleEffect.Parameters["worldMatrix"];
            mViewMatrix = mSimpleEffect.Parameters["viewMatrix"];
            mProjectionMatrix = mSimpleEffect.Parameters["projectionMatrix"];
            mWorldMatrixInvTrans = mSimpleEffect.Parameters["worldMatrixInvTrans"];

            // Camera
            mCameraPosition = mSimpleEffect.Parameters["cameraPosition"];

            // Flags
            mTextureFlags = mSimpleEffect.Parameters["textureFlags"];

            // Colors
            mDiffuseColor = mSimpleEffect.Parameters["diffuseColor"];
            mSpecularColorPower = mSimpleEffect.Parameters["specularColorPower"];
            mEmissiveColor = mSimpleEffect.Parameters["emissiveColor"];

            // Light Data
            mLightNum = mSimpleEffect.Parameters["lightNum"];

            for (int i = 0; i < mMaxLightNum; i++)
            {
                lightData[i].mLightData_Type = mSimpleEffect.Parameters["lightData"].Elements[i].StructureMembers[0];
                lightData[i].mLightData_Position = mSimpleEffect.Parameters["lightData"].Elements[i].StructureMembers[1];
                lightData[i].mLightData_Direction = mSimpleEffect.Parameters["lightData"].Elements[i].StructureMembers[2];
                lightData[i].mLightData_DiffuseColor = mSimpleEffect.Parameters["lightData"].Elements[i].StructureMembers[3];
                lightData[i].mLightData_LightParams = mSimpleEffect.Parameters["lightData"].Elements[i].StructureMembers[4];
                lightData[i].mLightData_IsGeneratingShadows = mSimpleEffect.Parameters["lightData"].Elements[i].StructureMembers[5];
            }

            mAmbientColorPower = mSimpleEffect.Parameters["ambientColorPower"];

            // Textures
            mDiffuseTexture = mSimpleEffect.Parameters["diffuseTexture"];
            mNormalTexture = mSimpleEffect.Parameters["normalTexture"];
            mSpecularTexture = mSimpleEffect.Parameters["specularTexture"];
            mParallaxTexture = mSimpleEffect.Parameters["parallaxTexture"];
            mShadowMapTexture = mSimpleEffect.Parameters["shadowMapTexture"];

            mParallaxScaleBias = mSimpleEffect.Parameters["parallaxScaleBias"];

        }

        /// <summary>
        /// Sends the view related parameters to the associated instance of SimpleEffect.
        /// </summary>
        /// <param name="view">The current view matrix.</param>
        /// <param name="projection">The current projection matrix.</param>
        /// <param name="cameraPos">The current camera position.</param>
        public void SetViewData(Matrix view, Matrix projection, Vector3 cameraPos, Vector4 ambientColorPower)
        {
            mViewMatrix.SetValue(view);
            mProjectionMatrix.SetValue(projection);
            mCameraPosition.SetValue(cameraPos);
            mAmbientColorPower.SetValue(ambientColorPower);
        }

        /// <summary>
        /// Sends the light related parameters to the associated instance of SimpleEffect.
        /// </summary>
        /// <param name="lights"> An array of lights with thier parameters to be sent to the associated instance of SimpleEffect.</param>
        public void SetLightData(Light[] lights)
        {
            mLightNum.SetValue(lights.Length);
            for (int i = 0; i < lights.Length; i++)
            {
                lightData[i].mLightData_Type.SetValue(((int)lights[i].LType));
                lightData[i].mLightData_Position.SetValue(lights[i].Position);
                lightData[i].mLightData_Direction.SetValue(lights[i].Direction);
                lightData[i].mLightData_DiffuseColor.SetValue(lights[i].DiffuseColor);
                lightData[i].mLightData_LightParams.SetValue(lights[i].LightParams);
                lightData[i].mLightData_IsGeneratingShadows.SetValue(lights[i].IsGeneratingShadows);
            }
        }

        /// <summary>
        /// Sends the object related parameters to the associated instance of SimpleEffect.
        /// </summary>
        /// <param name="world"> The world matrix of the current mesh part.</param>
        /// <param name="material">The material connected with the current mesh part.</param>
        public void SetObjectData(ref Matrix world, Material material)
        {
            mWorldMatrix.SetValue(world);
            Matrix.Invert(ref world, out world);
            Matrix.Transpose(ref world, out world);
            mWorldMatrixInvTrans.SetValue(world);
            //mWorldMatrixInvTrans.SetValue(Matrix.Transpose(Matrix.Invert(world)));

            mTextureFlags.SetValue(new bool[4] { material.IsTextured, material.IsNormalMapped, material.IsSpecularMapped, material.IsParallaxMapped });
            if (material.IsTextured)
            {
                mDiffuseTexture.SetValue(material.DiffuseTexture);
            }
            else
            {
                mDiffuseColor.SetValue(material.DiffuseColor);
            }

            if (material.IsSpecularMapped)
            {
                mSpecularTexture.SetValue(material.SpecularTexture);
            }

            if (material.IsNormalMapped)
            {
                mNormalTexture.SetValue(material.NormalTexture);
            }

            if (material.IsParallaxMapped)
            {
                mParallaxTexture.SetValue(material.ParallaxTexture);
                mParallaxScaleBias.SetValue(material.ParallaxScaleBias);
            }

            mSpecularColorPower.SetValue(material.SpecularColorPower);
            mEmissiveColor.SetValue(material.EmissiveColor);
        }

        public void SetShadowMap(Texture2D shadowMap)
        {
            mShadowMapTexture.SetValue(shadowMap);
        }
    }
}
